﻿using System;
using System.Collections.Generic;
using System.Text;

namespace RaisingStudio.Data
{
    /// <summary>
    /// dataset.
    /// </summary>
    [DefinitionName("DataSet")]
    [Serializable]
    public partial class DataSet : System.Data.DataSet
    {
        #region static member
        /// <summary>
        /// Initialize.
        /// </summary>
        /// <param name="dataSet">dataset.</param>
        /// <param name="xmlElement">xml element.</param>
        /// <param name="system">Use System.Data.DataTable as DataTable and System.Data.DataColumn as Data column.</param>
        public static void InitDataSet(System.Data.DataSet dataSet, System.Type type, System.Xml.XmlElement xmlElement, bool system)
        {
            // Sets dataset name 。
            dataSet.DataSetName = xmlElement.Attributes["DataSetName"].Value;
            #region Other property 
            if (xmlElement.Attributes["CaseSensitive"] != null)
            {
                dataSet.CaseSensitive = bool.Parse(xmlElement.Attributes["CaseSensitive"].Value);
            }
            if (xmlElement.Attributes["EnforceConstraints"] != null)
            {
                dataSet.EnforceConstraints = bool.Parse(xmlElement.Attributes["EnforceConstraints"].Value);
            }
            if (xmlElement.Attributes["Locale"] != null)
            {
                dataSet.Locale = new System.Globalization.CultureInfo(xmlElement.Attributes["Locale"].Value);
            }
            if (xmlElement.Attributes["Namespace"] != null)
            {
                dataSet.Namespace = xmlElement.Attributes["Namespace"].Value;
            }
            if (xmlElement.Attributes["Prefix"] != null)
            {
                dataSet.Prefix = xmlElement.Attributes["Prefix"].Value;
            }
#if (PocketPC || Smartphone || WindowsCE)
#else
            if (xmlElement.Attributes["RemotingFormat"] != null)
            {
                dataSet.RemotingFormat = (System.Data.SerializationFormat)Enum.Parse(typeof(System.Data.SerializationFormat), xmlElement.Attributes["RemotingFormat"].Value);
            }
            if (xmlElement.Attributes["SchemaSerializationMode"] != null)
            {
                dataSet.SchemaSerializationMode = (System.Data.SchemaSerializationMode)Enum.Parse(typeof(System.Data.SchemaSerializationMode), xmlElement.Attributes["SchemaSerializationMode"].Value);
            }
#endif
            #endregion

            foreach (System.Xml.XmlNode childXmlNode in xmlElement)
            {
                if (childXmlNode is System.Xml.XmlElement)
                {
                    System.Xml.XmlElement childXmlElement = childXmlNode as System.Xml.XmlElement;
                    switch (childXmlElement.Name)
                    {
                        case "Tables":
                            {
                                AddTables(dataSet, type, childXmlElement, system);
                                break;
                            }
                        case "Relations":
                            {
                                AddRelations(dataSet, type, childXmlElement);
                                break;
                            }
                    }
                }
            }
        }
        
        /// <summary>
        /// Initialize.
        /// </summary>
        /// <param name="dataSet">dataset.</param>
        /// <param name="type">Type.</param>
        public static void InitDataSet(System.Data.DataSet dataSet, System.Type type)
        {
            System.Xml.XmlElement xmlElement = DefinitionNameAttribute.LoadXmlDocument(type);
            InitDataSet(dataSet, type, xmlElement, false);
        }

        /// <summary>
        /// Initialize.
        /// </summary>
        /// <param name="dataSet">dataset.</param>
        /// <param name="type">Type.</param>
        /// <param name="system">Use System.Data.DataTable as DataTable and System.Data.DataColumn as Data column.</param>
        public static void InitDataSet(System.Data.DataSet dataSet, System.Type type, bool system)
        {
            System.Xml.XmlElement xmlElement = DefinitionNameAttribute.LoadXmlDocument(type);
            InitDataSet(dataSet, type, xmlElement, system);
        }

        /// <summary>
        /// Initialize.
        /// </summary>
        /// <param name="dataSet">table.</param>
        /// <param name="type">Type.</param>
        /// <param name="name">name.</param>
        public static void InitDataSet(System.Data.DataSet dataSet, System.Type type, string name)
        {
            System.Xml.XmlElement xmlElement = DefinitionNameAttribute.LoadXmlDocument(type, name);
            InitDataSet(dataSet, type, xmlElement, false);
        }

        /// <summary>
        /// Initialize.
        /// </summary>
        /// <param name="dataSet">table.</param>
        /// <param name="type">Type.</param>
        /// <param name="name">name.</param>
        /// <param name="system">Use System.Data.DataTable as DataTable and System.Data.DataColumn as Data column.</param>
        public static void InitDataSet(System.Data.DataSet dataSet, System.Type type, string name, bool system)
        {
            System.Xml.XmlElement xmlElement = DefinitionNameAttribute.LoadXmlDocument(type, name);
            InitDataSet(dataSet, type, xmlElement, system);
        }


        /// <summary>
        /// Add table.
        /// </summary>
        /// <param name="dataSet">dataset.</param>
        /// <param name="xmlElement">xml element.</param>
        public static void AddTables(System.Data.DataSet dataSet, System.Type type, System.Xml.XmlElement xmlElement, bool system)
        {
            // Add table.
            foreach (System.Xml.XmlNode childXmlNode in xmlElement)
            {
                if (childXmlNode is System.Xml.XmlElement)
                {
                    System.Xml.XmlElement childXmlElement = childXmlNode as System.Xml.XmlElement;
                    System.Data.DataTable dataTable = CreateDataTable(dataSet, type, childXmlElement, system);
                    dataSet.Tables.Add(dataTable);
                }
            }
        }

        /// <summary>
        /// Add Relation 。
        /// </summary>
        /// <param name="dataSet">dataset.</param>
        /// <param name="xmlElement">xml element.</param>
        public static void AddRelations(System.Data.DataSet dataSet, System.Type type, System.Xml.XmlElement xmlElement)
        {
            // Add Relation 。
            foreach (System.Xml.XmlNode childXmlNode in xmlElement)
            {
                if (childXmlNode is System.Xml.XmlElement)
                {
                    System.Xml.XmlElement childXmlElement = childXmlNode as System.Xml.XmlElement;
                    System.Data.DataRelation dataRelation = CreateDataRelation(dataSet, childXmlElement);
                    #region Other property 
                    if (dataRelation.ChildKeyConstraint != null)
                    {
                        System.Data.ForeignKeyConstraint foreignKeyConstraint = dataRelation.ChildKeyConstraint;
                        if (childXmlElement.Attributes["AcceptRejectRule"] != null)
                        {
                            foreignKeyConstraint.AcceptRejectRule = (System.Data.AcceptRejectRule)System.Enum.Parse(typeof(System.Data.AcceptRejectRule), childXmlElement.Attributes["AcceptRejectRule"].Value, false);
                        }
                        if (childXmlElement.Attributes["DeleteRule"] != null)
                        {
                            foreignKeyConstraint.DeleteRule = (System.Data.Rule)System.Enum.Parse(typeof(System.Data.Rule), childXmlElement.Attributes["DeleteRule"].Value, false);
                        }
                        if (childXmlElement.Attributes["UpdateRule"] != null)
                        {
                            foreignKeyConstraint.UpdateRule = (System.Data.Rule)System.Enum.Parse(typeof(System.Data.Rule), childXmlElement.Attributes["UpdateRule"].Value, false);
                        }
                    }
                    #endregion
                    dataSet.Relations.Add(dataRelation);
                }
            }
        }

        /// <summary>
        /// Create Relation 。
        /// </summary>
        /// <param name="dataSet">dataset.</param>
        /// <param name="xmlElement">xml element.</param>
        /// <returns>Relation 。</returns>
        public static System.Data.DataRelation CreateDataRelation(System.Data.DataSet dataSet, System.Xml.XmlElement xmlElement)
        {
            string relationName = xmlElement.Attributes[DefinitionAttributeNames.RelationName].Value;
            string parentTableName = xmlElement.Attributes[DefinitionAttributeNames.ParentTable].Value;
            string parentColumnNames = xmlElement.Attributes[DefinitionAttributeNames.ParentColumns].Value;
            string childTableName = xmlElement.Attributes[DefinitionAttributeNames.ChildTable].Value;
            string childColumnNames = xmlElement.Attributes[DefinitionAttributeNames.ChildColumns].Value;

            System.Data.DataTable parentTable = dataSet.Tables[parentTableName];
            System.Data.DataColumn[] parentColumns = GetDataColumns(parentTable, parentColumnNames);
            System.Data.DataTable childTable = dataSet.Tables[childTableName];
            System.Data.DataColumn[] childColumns = GetDataColumns(childTable, childColumnNames);

            System.Data.DataRelation dataRelation;
            if (xmlElement.Attributes[DefinitionAttributeNames.CreateConstraints] != null)
            {
                dataRelation = new System.Data.DataRelation(relationName, parentColumns, childColumns, bool.Parse(xmlElement.Attributes[DefinitionAttributeNames.CreateConstraints].Value));
            }
            else
            {
                dataRelation = new System.Data.DataRelation(relationName, parentColumns, childColumns);
            }
            #region Other property 
            if (xmlElement.Attributes["Nested"] != null)
            {
                dataRelation.Nested = bool.Parse(xmlElement.Attributes["Nested"].Value);
            }
            #endregion
            return dataRelation;
        }

        /// <summary>
        /// Gets array of columns.
        /// </summary>
        /// <param name="dataTable">table.</param>
        /// <param name="columnNames">column name.</param>
        /// <returns>array of columns.</returns>
        public static System.Data.DataColumn[] GetDataColumns(System.Data.DataTable dataTable, string columnNames)
        {
            if (columnNames.IndexOf(",") >= 0)
            {
                return GetDataColumns(dataTable, columnNames.Split(','));
            }
            else
            {
                return new System.Data.DataColumn[] { dataTable.Columns[columnNames] };
            }
        }

        /// <summary>
        /// Gets array of columns.
        /// </summary>
        /// <param name="dataTable">table.</param>
        /// <param name="columnNames">column name array.</param>
        /// <returns>array of columns.</returns>
        public static System.Data.DataColumn[] GetDataColumns(System.Data.DataTable dataTable, string[] columnNames)
        {
            System.Data.DataColumn[] dataColumns = new System.Data.DataColumn[columnNames.Length];
            for (int i = 0; i < columnNames.Length; i++)
            {
                dataColumns[i] = dataTable.Columns[columnNames[i]];
            }
            return dataColumns;
        }

        /// <summary>
        /// Gets datatable Type.
        /// </summary>
        /// <param name="dataSet">dataset.</param>
        /// <param name="typeName">Type name.</param>
        /// <returns>datatable Type.</returns>
        public static System.Type GetDataTableType(System.Data.DataSet dataSet, System.Type type, string typeName)
        {
#if USE_TYPEMANAGER
            return RaisingStudio.Reflection.TypeManager.GetType(dataSet, typeName);
#else
            System.Type dataTableType;
            System.Reflection.Assembly assembly;
            if (typeName.IndexOf(",") >= 0)
            {
                string[] names = typeName.Split(',');
                assembly = System.Reflection.Assembly.LoadFrom(names[0]);
                typeName = names[1];
            }
            else
            {
                assembly = type.Assembly;
            }

            dataTableType = assembly.GetType(typeName);
            return dataTableType;
#endif
        }

        /// <summary>
        /// Create table.
        /// </summary>
        /// <param name="dataSet">dataset.</param>
        /// <param name="xmlElement">xml element.</param>
        /// <returns>table.</returns>
        public static System.Data.DataTable CreateDataTable(System.Data.DataSet dataSet, System.Type type, System.Xml.XmlElement xmlElement, bool system)
        {
            System.Data.DataTable dataTable;
            System.Type dataTableType;
            if (xmlElement.Attributes["Type"] != null)
            {
                string typeName = xmlElement.Attributes["Type"].Value.Trim();
                // 通过type Create table.
                dataTableType = GetDataTableType(dataSet, type, typeName);
                if (dataTableType.IsSubclassOf(typeof(System.Data.DataTable)))
                {
                    dataTable = (System.Activator.CreateInstance(dataTableType) as System.Data.DataTable);
                   
                    if (xmlElement.Attributes["DefinitionName"] != null)
                    {
                        string definitionName = xmlElement.Attributes["DefinitionName"].Value.Trim();
                        // 使用definition 初始化table.
                        DataTable.InitDataTable(dataTable, dataTableType, definitionName, system);
                    }
                }
                else
                {
                    if (system)
                    {
                        dataTable = new System.Data.DataTable();
                    }
                    else
                    {
                        dataTable = new RaisingStudio.Data.DataTable();
                    }

                    if (xmlElement.Attributes["DefinitionName"] != null)
                    {
                        string definitionName = xmlElement.Attributes["DefinitionName"].Value.Trim();
                        // 使用definition 初始化table.
                        DataTable.InitDataTable(dataTable, dataTableType, definitionName, system);
                    }
                    else
                    {
                        DataTable.InitDataTable(dataTable, dataTableType, system);
                    }
                }
            }
            else
            {
                if (system)
                {
                    dataTable = new System.Data.DataTable();
                }
                else
                {
                    dataTable = new RaisingStudio.Data.DataTable();
                }

                if (xmlElement.Attributes["DefinitionName"] != null)
                {
                    string definitionName = xmlElement.Attributes["DefinitionName"].Value.Trim();
                    // 使用definition 初始化table.
                    DataTable.InitDataTable(dataTable, type, definitionName, system);
                }
            }


            // Sets table name.
            string tableName = xmlElement.Attributes["TableName"].Value.Trim();
            dataTable.TableName = tableName;

            return dataTable;
        }
        #endregion
        #region Constructor
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="type">Type.</param>
        public DataSet(System.Type type)
        {
            InitDataSet(this, type);
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="type">Type.</param>
        /// <param name="name">name.</param>
        public DataSet(System.Type type, string name)
        {
            InitDataSet(this, type, name);
        }
        #endregion
    }
}
